1. 如何理解原型和原型链
js 的所有对象都继承自原型对象,每个对象上都有一个私有属性指向另一个原型对象,原型对象上也有一个自己的原型,依此类推,直到一个对象的原型是 null,根据定义 null 没有原型,所以它就是这个原型链的最后一个节点。
2. [] 的原型链是怎样的?
[] > Array > Object > null
3. __proto__
[[Prototype]]
prototype 区别是什么
js 中,每个物件都有一个内部属性 [[Prototype]]
来标识物件的原型,但是这个内部属性是不能直接访问到的,浏览器就实现了非标准的访问方法 __proto__
, 实际开发中建议通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 函数来访问。
prototype
表示的是函数 func.prototype, 是存在于构造函数中的一个属性,构造函数的 prototype
和 __proto__
指向同一个原型对象。
1 2 3 4 5 6 7 8 9
| function Person() {} let personA = new Person();
console.log(Person.prototype.constructor.name);
personA.__proto__ === Person.prototype; Object.getPrototypeOf(personA) === Person.prototype; personA.__proto__ === Object.getPrototypeOf(personA);
|
4. js 继承有哪几种方式
1. 原型链继承
1 2 3 4 5 6 7 8 9 10 11 12
| function Animal() {}
const cat = new Animal();
Animal.prototype.sleep = function () { console.log('sleep'); };
cat.sleep();
|
主要问题:
- 引用类型的属性被所有实例共享
- 在创建 cat 实例的时候,无法向 Animal 构造函数中传参
2.构造函数继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function Super() { this.colors = ['green', 'blue', 'red']; }
function Sub() { Super.call(this); }
let sub1 = new Sub(); sub1.colors.push('yellow'); console.log(sub1.colors);
let sub2 = new Sub(); console.log(sub2.colors);
|
解决了原型继承中实例共享和无法向父构造函数传参的问题,但是方法都定义在构造函数中,每次创建实例都会创建一次所有方法。
3. 组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function Super(name, age) { this.name = name || 'xiaoming'; this.age = age || 30; }
function Sub(name, age) { Super.call(this, name, age); }
Super.prototype.hello = function() { return `${this.name} has ${this.age}`; }
Sub.prototype = new Super(); Sub.prototype.constructor = Sub; let instance = new Sub('xiaohong', 18); instance.hello();
|
组合继承结合了原型链和构造函数继承的优点,函数可复用,可向父构造函数传参,实例属性不共享,但是缺点是父构造函数调用了两次。
4. 寄生组合式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function Super(name, age) { this.name = name || 'xiaoming'; this.age = age || 30; }
function Sub(name, age) { Super.call(this, name, age); }
Super.prototype.hello = function() { return `${this.name} has ${this.age}`; }
Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub;
Sub.prototype.getName = function() { return this.name; }
let instance = new Sub('xiaohong', 18); instance.hello(); instance.getName();
|
该继承方式弥补了组合式继承调用两次父类的问题,也就是我们常用的 es5 继承方法。
5.类继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class People { constructor(name, age) { this.name = name; this.age = age; }
hello() { console.log(`${this.name} has ${this.age}`); } }
class Woman extends People { constructor(name, age) { super(name, age); }
hello() { super.hello(); } }
let girl = new Woman('lili', 18); girl.hello();
|
class 实际上就是 es5 构造函数的语法糖,通过 new 关键字创建实例,通过 extends 实现继承,extends 的作用就是将 Woman.prototype 指向 People. extends 的实现逻辑其实就是来自于寄生组合式继承方法。
es6 通过 babel 编译成 es5,其中实现继承的逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|